package compiler.emiter;

import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import compiler.common.Method;
import compiler.common.TypeTC;
import compiler.emiter.expressiontypes.*;

public class EmiterEnv {
    Map<String, String> functionToSig;
    public String currentClass;
    public String currentFun;
    public VariableEnumerator variableEnumerator;
    public List<TypeTC> declaredTypes;
    public List<Method> globalFuns;
    
    public String consArraysInit;
    public String localArraysInit;
        
    
    public EmiterEnv(HashMap<String,String> map, List<TypeTC> types, List<Method> funs) {
      this.functionToSig = map;
      this.currentClass = "";
      this.currentFun = "";
      this.variableEnumerator = new VariableEnumerator();
      this.declaredTypes = types;
      this.globalFuns = funs;
      this.consArraysInit = "";
    }    

    public boolean hasGlobalFun(String name) {
      for (Method m : this.globalFuns) {
        if(m.getName().equals(name))
          return true;
      }
      return false;
    }

    public String getGlobalFunSig(String name) {
      for (Method m : this.globalFuns) {
        if(m.getName().equals(name))
          return m.getEmiterSignature("", true);
      }
      return "";
    }
    
    // generates code for tables intializing
    public void arraysCode() {
      this.consArraysInit = "";
      this.localArraysInit = "";
      Set<String> keys = this.variableEnumerator.variableToNumber.keySet();
      for(String name : keys) {
        Variable var = this.variableEnumerator.variableToNumber.get(name);
        if(var.type.isArrayType && var.type.arraySize > 0)
        {
          String prefix = 
              var.type instanceof StringType || var.type instanceof ReferenceType ?
                    "a" : "";
          String type = var.type instanceof IntegerType ? "int" :
                        var.type instanceof BooleanType ? "boolean" :
                        var.type instanceof CharacterType ? "char" :
                        var.type instanceof StringType ? "java/lang/String" :
                        var.type instanceof RealType ? "float" :
                        var.type instanceof ReferenceType ? 
                              ((ReferenceType)var.type).name : "error";
          if(!var.isField ) {
            this.localArraysInit += "ldc " + var.type.arraySize + "\n";
            this.localArraysInit += prefix + "newarray " + type + "\n";
            this.localArraysInit += "astore " + var.number + "\n\n";
          }
          else {
            String type2 = var.type instanceof IntegerType ? "I" :
              var.type instanceof BooleanType ? "Z" :
              var.type instanceof CharacterType ? "C" :
              var.type instanceof StringType ? "Ljava/lang/String;" :
              var.type instanceof RealType ? "F" :
              var.type instanceof ReferenceType ? 
                    "L" + ((ReferenceType)var.type).name + ";" : "error";
            this.consArraysInit +=  "aload_0\n";
            this.consArraysInit += "ldc " + var.type.arraySize + "\n";
            this.consArraysInit += prefix + "newarray " + type + "\n";
            this.consArraysInit += "putfield " + this.currentClass + 
                                    "/" + var.name + " [" + type2 + "\n\n";
          }
        }
      }
    }
    
    public String getFunRetType(String className, String funName, int arity) {
      for(TypeTC t : declaredTypes) {
        if(t.getName().equals(className)) {
          String ret = t.methodReturnType(funName, arity);
          if(ret.startsWith("array of ")) {
              ret=ret.substring(9);
              ret = "[" + ret;
          }
          return ret;          
        }
      }
      return null;
    }
    
    public String getFunSig(String clas, String fun, int ar) {
      return this.functionToSig.get(clas + "$" + fun + "$" + ar);
    }
    
    public int varSize() {
      return this.variableEnumerator.size();
    }
    
    private TypeTC findType(String name) {
      for(TypeTC type : this.declaredTypes) {
        if (name == type.getName())
           return type;
      }
      return null;
    }
    
    public boolean isDefined(String typeName)
    {    
      for(TypeTC type : declaredTypes) {
        if(typeName.equals(type.getName()))
          return true;
      }
      return false;
    }
    
    public boolean hasStdConsImpl(String name) {
      return this.findType(name).hasDefaultInitImpl;
    }
}
